# _*_coding:utf-8_*_

import os
import queue as Queue
import threading
import time
import traceback

from ATS.datamanager import DmConstant
from ATS.globaldata import gdata
from utils.log_util import loggings as atsLogger
from ATS.util import constant
from . import DbImpl

MAXSIZE = 1000
TRANSACTION_NUMBER = 1500

global DB_SERVER
DB_SERVER = None


def get_dbServer():
    global DB_SERVER
    if DB_SERVER is None:
        DB_SERVER = DbServer()
    return DB_SERVER


def init_dbServer(db_f):
    if not constant.ENABLE_DB_SERVER:
        return
    try:
        global DB_SERVER
        if DB_SERVER is not None:
            DB_SERVER.set_dbf(db_f)
            DB_SERVER.in_run = True
        else:

            DB_SERVER = DbServer(db_f)
            DB_SERVER.in_run = True
            DB_SERVER.start()
    except:
        atsLogger.error(traceback.format_exc())


def add_task(value):
    if not constant.ENABLE_DB_SERVER:
        return
    global DB_SERVER
    if DB_SERVER:
        DB_SERVER.addTask(value)


def Commit_db():
    if not constant.ENABLE_DB_SERVER:
        return

    if DB_SERVER is not None:
        DB_SERVER.force_commit = True


def stop():
    if not constant.ENABLE_DB_SERVER:
        return
    global DB_SERVER
    if DB_SERVER is not None:
        #        print('Enabbbbble stop flag')
        DB_SERVER.in_run = False


def isFinished():
    if not constant.ENABLE_DB_SERVER:
        return True
    global DB_SERVER
    if DB_SERVER is None:
        return True
    return DB_SERVER.is_task_list_finished()


class DbServer(threading.Thread):

    def __init__(self, db_f=None):
        """
        To be init in linerizePPf method
        """
        threading.Thread.__init__(self, name="DB_SERVER")
        self.setDaemon(True)
        self.db_con = None
        self.db_file = db_f
        self.taskQueue = Queue.Queue(0)
        self.cnt = 0
        self.in_run = False
        self.dbf_is_new = True
        self.force_commit = False
        self.num_to_commit = 0
        self.work_load_cnt = 0
        self.curr_loop = 0

    #        print("Finish init for DbServer")

    def init_db_conn(self):
        if not os.path.exists(self.db_file):
            atsLogger.error("DBServer setting db file error: %s dose not exist!" % self.db_file)
            return
        data_model = gdata.get_service(constant.DATA_MODEL_INSTANCE)
        self.db_con = DbImpl.DbImpl(self.db_file, 'DEFERRED')
        self.db_con.copyNew(data_model)
        self.dbf_is_new = False

    def refresh_db_conn(self):
        if self.db_con:
            self.db_con.release_con()
            self.db_con = None
        self.init_db_conn()

    def release(self):
        if self.db_con:
            #            self.db_con.release_con()
            self.db_con = None
        self.db_file = ""
        self.cnt = 0

    def set_dbf(self, db_f):
        self.db_file = db_f
        self.dbf_is_new = True

    def addTask(self, value):
        try:
            self.taskQueue.put_nowait(value)
        except:
            print("Error when adding saving data task: " + traceback.format_exc())
            atsLogger.error(traceback.format_exc())
            pass

    def run(self):
        try:
            while True:
                try:
                    task = self.taskQueue.get(False)
                except:
                    if self.db_con and self.force_commit:
                        atsLogger.debug("Beofre commit1")
                        self.db_con.register_misc_item(DmConstant.ITEM_IN_RUNNING, DmConstant.VALUE_FALSE)
                        self.db_con.db.commit()
                        atsLogger.debug("After commit1")
                        self.force_commit = False
                        time.sleep(3)
                        DbImpl.on_project_stoped()
                    time.sleep(0.5)
                    continue
                if task is None or len(task) == 0:
                    continue

                if self.db_con is None:
                    while not self.db_file or not os.path.exists(self.db_file):
                        print("Fatal Error: db file %s is not valid." % self.db_file)
                        time.sleep(1)
                        continue
                    self.init_db_conn()
                if self.dbf_is_new:
                    self.refresh_db_conn()

                self.processTask(task)
                self.cnt += 1
                if self.cnt % 100 == 1:
                    DbImpl.on_project_going(self.taskQueue.qsize())
        #                time.sleep(0.01)
        except:
            print("Error in dbserver thread: ", traceback.format_exc())
            atsLogger.error(traceback.format_exc())

    def processTask(self, task):
        try:
            if len(task) == 0:
                return
            task_type = task.keys()[0]
            task_value = task.values()[0]
            if task_type == constant.DB_INSERT_TYPE:
                if len(task_value) != 2:
                    atsLogger.error("Error: task value length should be 2")
                    return
                normal_things = task_value[0]
                lmd_ind = normal_things.get('locid')
                loop_id = normal_things.get('lopid')
                #                db_locid = normal_things.get('dbLocId')
                data = task_value[1]
                self.log_to_persist_model_n(lmd_ind, loop_id, data)
                self.num_to_commit = self.num_to_commit + 1
            elif task_type == constant.DB_UPDATE_TYPE:
                dev = task_value.get('dev')
                loop = task_value.get('loop')
                bin_value = task_value.get('bin_value')
                atsLogger.error("The binvalue isssssssssss : %s_%s" % (str(loop), str(bin_value)))
                self.registerBinResult_n(dev, loop, bin_value)
                self.num_to_commit = self.num_to_commit + 1
                if loop != self.curr_loop:
                    self.curr_loop = loop
                    self.work_load_cnt = self.work_load_cnt + 1
                if self.work_load_cnt == DmConstant.DBBASE_SIZE:
                    self.new_db_base()
                    self.work_load_cnt = 0
                    self.num_to_commit = 0
            if self.num_to_commit >= TRANSACTION_NUMBER:
                atsLogger.debug("Before commit2")
                self.db_con.db.commit()
                atsLogger.debug("After commit2")
                self.num_to_commit = 0
        except:
            print("Error in process db task: " + traceback.format_exc())
            atsLogger.error("Error in process db task: " + traceback.format_exc())

    def log_to_persist_model_n(self, lmd_ind, loop_id, data):
        #        print("Enter log_to_persist_model_n---------")
        try:
            location = gdata.getLMD().get(lmd_ind, None)
            if constant.CUSTOMIZE_FOR_LOOP == 1 and loop_id is not None:
                self.db_con.persist_data(loop_id, location, data)
            else:
                if loop_id is None:
                    index_loc_map = gdata.getGDD().get(constant.INDEX_LOC_MAP, {})
                    location_pkid = index_loc_map.get(lmd_ind, None)
                else:
                    location_pkid = self.db_con.register_loop_location(loop_id, *location)
                if location_pkid is not None:
                    self.db_con.persist_data(location_pkid, location, data)
                else:
                    print("Error in log_to_persist_model: location pkid is None!")
                    atsLogger.error("Error in log_to_persist_model: location pkid is None!")
        except:
            print("Error in log_to_persist_model: " + traceback.format_exc())
            atsLogger.error("Error in log_to_persist_model: " + traceback.format_exc())

    def registerBinResult_n(self, dev, loop, bin_value):

        try:
            value_dict = {"bin_sort": bin_value}
            realt = True
            if not self.in_run:
                realt = False
            self.db_con.update_report_value(dev, loop, value_dict, real_ti=realt, commit=False)
        #            print("Finish registerBinResult_n---------")
        except:
            print("Error in update_bin_sort: " + traceback.format_exc())
            atsLogger.error("Error in update_bin_sort: " + traceback.format_exc())

    def is_task_list_finished(self):
        #        print('self.taskQueue.empty(): ',self.taskQueue.empty())
        return self.taskQueue.empty()

    def new_db_base(self):
        if self.db_con and self.db_con.db:
            self.db_con.register_misc_item(DmConstant.ITEM_IN_RUNNING, DmConstant.VALUE_FALSE)
            self.db_con.db.commit()
            self.db_con.db.close()
        dir_v = os.path.dirname(self.db_file)
        f_v = os.path.basename(self.db_file)
        p_file_name = f_v.rstrip(".db")
        if p_file_name.count("_") > 0:
            time_sec = p_file_name[p_file_name.rindex("_") + 1:]
            ppf_lot_id = p_file_name[0:p_file_name.rindex("_")]
            lot_id = ppf_lot_id[ppf_lot_id.rindex("_") + 1:]
            if len(time_sec) < 16:
                atsLogger.error("Error: DB file name is error: %s" % p_file_name)
                return False

            try:
                tiem_Value = time_sec[0:14]
                index = int(time_sec[15:])
                new_f = os.path.join(dir_v, "%s_%s$%d.db" % (ppf_lot_id, tiem_Value, index + 1))
                self.set_dbf(new_f)

                data_model = gdata.get_service(constant.DATA_MODEL_INSTANCE)
                self.db_con = DbImpl.DbImpl(self.db_file, 'DEFERRED')
                self.db_con.copyNew(data_model)
                self.dbf_is_new = False
                self.db_con.db.commit()

                curr_ppf = gdata.get_service(constant.CURR_PPF_KEY)
                ppf_vv = curr_ppf
                if curr_ppf.count(".") > 0:
                    ppf_vv = curr_ppf[0:curr_ppf.rindex(".")]
                self.db_con.on_project_run(ppf_vv, is_append=False, batch_num=lot_id)
                self.db_con.update_project_run(self.db_con.prj_run_id)
                self.db_con.register_misc_item(DmConstant.ITEM_IN_RUNNING, DmConstant.VALUE_TRUE)
                self.db_con.db.commit()

                output_models_dic = gdata.get_service(constant.OUTPUT_MODELS_KEY)
                if output_models_dic is None:
                    atsLogger.error("Error: output_models_dic is None ")
                    return False
                self.db_con.register_output_models(output_models_dic)
                self.db_con.db.commit()

            except:
                atsLogger.error("Error: DB file name is error: %s" % p_file_name)
                return False
